/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Globalization;
using System.Xml.XPath;
using DaveSexton.DocProject.Sandcastle.BuildComponents.TypeConverters.BuiltIn;
using System.Xml;

namespace DaveSexton.DocProject.Sandcastle.BuildComponents.Editors.BuiltIn
{
	internal sealed partial class TransformComponentEditorDialog : DaveSexton.DocProject.Sandcastle.BuildComponents.Editors.CancelableBaseEditorDialog
	{
		#region Public Properties
		#endregion

		#region Private / Protected
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="TransformComponentEditorDialog" /> class.
		/// </summary>
		public TransformComponentEditorDialog()
		{
			InitializeComponent();
		}
		#endregion

		#region Methods
		protected override void UpdateForBuildComponent(SandcastleBuildComponent component)
		{
			SuspendLayout();

			try
			{
				DataSet data = new DataSet();
				data.Locale = System.Globalization.CultureInfo.CurrentCulture;

				DataTable filesTable = CreateFilesDataTable(data);
				DataTable argumentsTable = CreateArgumentsDataTable(data);

				DataRelation relation = data.Relations.Add(filesTable.Columns[0], argumentsTable.Columns[0]);

				FillDataTables(component, filesTable, argumentsTable);

				filesBindingSource.DataSource = filesTable;

				argumentsBindingSource.DataMember = relation.RelationName;
				argumentsBindingSource.DataSource = filesBindingSource;
			}
			finally
			{
				ResumeLayout();
			}
		}

		private static DataTable CreateFilesDataTable(DataSet data)
		{
			DataTable table = data.Tables.Add();
			table.Locale = System.Globalization.CultureInfo.CurrentCulture;

			DataColumn column = table.Columns.Add("file", typeof(string));
			column.AllowDBNull = false;
			column.DefaultValue = string.Empty;

			return table;
		}

		private static DataTable CreateArgumentsDataTable(DataSet data)
		{
			DataTable table = data.Tables.Add();
			table.Locale = System.Globalization.CultureInfo.CurrentCulture;

			DataColumn column = table.Columns.Add("file", typeof(string));		// foreign key
			column.AllowDBNull = false;
			column.DefaultValue = string.Empty;

			column = table.Columns.Add("key", typeof(string));
			column.AllowDBNull = false;
			column.DefaultValue = string.Empty;

			column = table.Columns.Add("value", typeof(string));
			column.AllowDBNull = false;
			column.DefaultValue = string.Empty;

			column = table.Columns.Add("expand-value", typeof(bool));
			column.AllowDBNull = false;
			column.DefaultValue = false;

			column = table.Columns.Add("xmlValue", typeof(string));
			column.AllowDBNull = false;
			column.DefaultValue = string.Empty;

			return table;
		}

		private static void FillDataTables(IXPathNavigable component, DataTable filesTable, DataTable argumentsTable)
		{
			XPathNavigator navigator = component.CreateNavigator();

			foreach (XPathNavigator transform in navigator.SelectChildren("transform", ""))
			{
				string file = transform.GetAttribute("file", "");

				if (!string.IsNullOrEmpty(file))
				{
					DataRow row = filesTable.NewRow();
					row["file"] = file;
					filesTable.Rows.Add(row);

					FillArgumentsDataTable(transform, row, argumentsTable);
				}
			}
		}

		private static void FillArgumentsDataTable(IXPathNavigable transform, DataRow parent, DataTable table)
		{
			XPathNavigator navigator = transform.CreateNavigator();

			foreach (XPathNavigator content in navigator.SelectChildren("argument", ""))
			{
				DataRow row = table.NewRow();
				row["key"] = content.GetAttribute("key", "");
				row["value"] = content.GetAttribute("value", "");

				bool expand;
				if (bool.TryParse(content.GetAttribute("expand-value", ""), out expand))
					row["expand-value"] = expand;

				row["xmlValue"] = FormatXml(content.InnerXml);

				row.SetParentRow(parent);
				table.Rows.Add(row);
			}
		}

		private static string FormatXml(string xml)
		{
			XmlDocument document = new XmlDocument();
			XmlNode root = document.AppendChild(document.CreateElement("root"));

			// remove leading white space
			root.InnerXml = xml;
			return root.InnerXml;
		}

		protected override bool SaveChanges()
		{
			if (!ValidateData())
				return false;

			XPathNavigator navigator = BuildComponent.CreateNavigator();
			StringBuilder output = new StringBuilder(Math.Max(navigator.InnerXml.Length, 256));

			XmlWriterSettings settings = new XmlWriterSettings();
			settings.ConformanceLevel = ConformanceLevel.Fragment;
			settings.Indent = true;

			using (XmlWriter writer = XmlWriter.Create(output, settings))
			{
				DataTable table = filesBindingSource.DataSource as DataTable;

				if (table != null)
					WriteTransformElements(writer, table);
			}

			navigator.InnerXml = output.ToString();

			return true;
		}

		private static void WriteTransformElements(XmlWriter writer, DataTable table)
		{
			foreach (DataRow row in table.Rows)
			{
				writer.WriteStartElement("transform");
				writer.WriteAttributeString(null, "file", null, (string) row["file"]);

				foreach (DataRow argRow in row.GetChildRows(table.ChildRelations[0]))
				{
					writer.WriteStartElement("argument");
					writer.WriteAttributeString(null, "key", null, (string) argRow["key"]);

					string value = (string) argRow["value"];

					if (!string.IsNullOrEmpty(value))
						writer.WriteAttributeString(null, "value", null, value);

					bool expand = (bool) argRow["expand-value"];

					if (expand)
						writer.WriteAttributeString(null, "expand-value", null, "true");

					string xml = (string) argRow["xmlValue"];

					if (xml != null && (xml = xml.Trim()).Length > 0)
						writer.WriteRaw(xml);

					writer.WriteEndElement();
				}

				writer.WriteEndElement();
			}
		}

		private bool ValidateData()
		{
			foreach (DataGridViewRow row in filesDataGridView.Rows)
				foreach (DataGridViewCell cell in row.Cells)
					if (cell.ErrorText.Length > 0)
						return false;

			foreach (DataGridViewRow row in argumentsDataGridView.Rows)
				foreach (DataGridViewCell cell in row.Cells)
					if (cell.ErrorText.Length > 0)
						return false;

			return true;
		}
		#endregion

		#region Event Handlers
		private void filesDataGridView_DataError(object sender, DataGridViewDataErrorEventArgs e)
		{
			e.Cancel = true;
		}

		private void argumentsDataGridView_DataError(object sender, DataGridViewDataErrorEventArgs e)
		{
			e.Cancel = true;
		}

		private void filesDataGridView_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
		{
			if (e.RowIndex > -1)
			{
				DataGridViewRow gridRow = filesDataGridView.Rows[e.RowIndex];

				if (gridRow.IsNewRow)
					return;

				DataRowView row = (DataRowView) gridRow.DataBoundItem;

				string file = row["file"] as string ?? "";

				if (file.Trim().Length == 0)
					gridRow.Cells[fileColumn.DisplayIndex].ErrorText = "File is required.";
				else
					gridRow.Cells[fileColumn.DisplayIndex].ErrorText = null;
			}
		}

		private void argumentsDataGridView_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
		{
			if (e.RowIndex > -1)
			{
				DataGridViewRow gridRow = argumentsDataGridView.Rows[e.RowIndex];

				if (gridRow.IsNewRow)
					return;

				DataRowView row = (DataRowView) gridRow.DataBoundItem;

				string key = row["key"] as string ?? "";
				bool keyValid = true;

				if (key.Trim().Length == 0)
				{
					keyValid = false;
					gridRow.Cells[keyColumn.DisplayIndex].ErrorText = "Key is required.";
				}
				else
				{
					try
					{
						System.Xml.Xsl.XsltArgumentList list = new System.Xml.Xsl.XsltArgumentList();
						list.AddParam(key, string.Empty, string.Empty);
					}
					catch (ArgumentException ex)
					{
						keyValid = false;
						gridRow.Cells[keyColumn.DisplayIndex].ErrorText = ex.Message;
					}
				}
				
				if (keyValid)
					gridRow.Cells[keyColumn.DisplayIndex].ErrorText = null;

				string xml = row["xmlValue"] as string ?? "";

				if (xml != null && (xml = xml.Trim()).Length > 0)
				{
					XmlDocument document = new XmlDocument();
					XmlNode root = document.AppendChild(document.CreateElement("root"));

					try
					{
						root.InnerXml = xml;
						gridRow.Cells[xmlValueColumn.DisplayIndex].ErrorText = null;
					}
					catch (XmlException ex)
					{
						gridRow.Cells[xmlValueColumn.DisplayIndex].ErrorText = ex.Message;
					}
				}
			}
		}
		#endregion
	}
}